package com.example.tys.oomdata.sqLiteData;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;
import android.util.Log;

import com.example.tys.oomdata.Ben;
import com.example.tys.oomdata.Test;

import java.lang.annotation.Annotation;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author tys
 * @date 2018/3/9
 */

public class DbHelper extends SQLiteOpenHelper {
    private static final String TAG = "DbHelper";
    public static String fullName;
    public static int version;
    private static DbHelper mInstance;
    private static Context mContext;
    private WeakReference<SQLiteDatabase> mSqLiteWeakRef;

    public static void init(Context context, String name, int version) {
        DbHelper.mContext = context.getApplicationContext();
        DbHelper.version = version;
        DbHelper.fullName = context.getDir("db", Context.MODE_PRIVATE) + "/" + name;
    }

    public static DbHelper getInstance() {
        if (mInstance == null) {
            synchronized (DbHelper.class) {
                if (mInstance == null) {
                    mInstance = new DbHelper(mContext, fullName, version);
                }
            }
        }
        return mInstance;
    }

    private DbHelper(Context context, String name, int version) {
        super(context, name, null, version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        Log.e(TAG, "onCreate");
        createdTable(db, Test.class);
        createdTable(db, Ben.class);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        Log.e(TAG, "onUpgrade:" + oldVersion + " -> " + newVersion);
    }

    @Override
    public SQLiteDatabase getWritableDatabase() {
        if (mSqLiteWeakRef == null || null == mSqLiteWeakRef.get()) {
            mSqLiteWeakRef = new WeakReference<>(super.getWritableDatabase());
        }
        return mSqLiteWeakRef.get();
    }

    @Override
    public SQLiteDatabase getReadableDatabase() {
        if (mSqLiteWeakRef == null || null == mSqLiteWeakRef.get()) {
            mSqLiteWeakRef = new WeakReference<>(super.getWritableDatabase());
        }
        return mSqLiteWeakRef.get();
    }

    /**
     * 确保在不适用时，关闭数据库句柄 (数据库句柄是个弱引用)
     * 关闭数据库句柄时如果其他线程在进行数据库操作:
     * Cannot perform this operation because the connection pool has been closed
     */
    public void closeSqliteDataBase() {
        if (mSqLiteWeakRef != null && mSqLiteWeakRef.get() != null) {
            mSqLiteWeakRef.get().close();
        }
    }

    private static void createdTable(SQLiteDatabase db, Class<?> entryClass) {
        String cmd = DbHelper.generalCreatedTabCmd(entryClass);
        Log.i(TAG, "CreateTable:" + cmd);
        db.execSQL(cmd);
    }

    private static String generalCreatedTabCmd(Class<?> entryClass) {
        String tabName = getTabName(entryClass);
        StringBuilder sb = new StringBuilder();
        sb.append("create table if not exists ");
        sb.append(tabName);
        sb.append("(");
//        (_id INTEGER NOT NULL PRIMARY KEY,name TEXT,password TEXT)
        Field[] fields = entryClass.getDeclaredFields();
        for (Field field : fields) {
            if (isFieldSaveToDb(field)) {
                sb.append(parseFieldInDbName(field)).append(" ");
                String cmd = parseFieldKey(field);
                if (TextUtils.isEmpty(cmd)) {
                    sb.append(parseFieldType(field)).append(",");
                } else {
                    sb.append(parseFieldType(field)).append(" ").append(cmd).append(",");
                }
            }
        }
        if (sb.charAt(sb.length() - 1) == ',') {
            sb.deleteCharAt(sb.length() - 1);
        }
        sb.append(")");
        return sb.toString();
    }

    /**
     * 该字段是否存入数据库
     *
     * @param field
     * @return true 该字段将回不被存入数据库
     */
    private static boolean isFieldSaveToDb(Field field) {
        if (Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers())) {
            return false;
        }
        return true;
    }

    /**
     * 解析在数据库中字段的类型
     *
     * @param field
     * @return
     */
    private static String parseFieldType(Field field) {
        Class type = field.getType();
        String sqlType = "";
        if (type == String.class) {
            sqlType = "text";
        } else if (type == Integer.class) {
            sqlType = "integer";
        } else if (type == Boolean.class) {
            sqlType = "byte";
        } else if (type == Long.class) {
            sqlType = "long";
        } else if (type == Double.class) {
            sqlType = "double";
        } else if (type == Float.class) {
            sqlType = "single";
        } else if (type == byte[].class) {
            sqlType = "blob";
        } else if (type == Short.class) {
            sqlType = "integer";
        } else {
            Log.e(TAG, "UnSupport type:" + type.getName());
        }
        return sqlType;
    }

    /**
     * 解析在数据库中字段的关键字(注解)
     *
     * @param field
     * @return
     */
    private static String parseFieldKey(Field field) {
        StringBuilder cmd = new StringBuilder("");
        Annotation[] annotations = field.getAnnotations();
        for (Annotation annotation : annotations) {
            if (annotation.annotationType() == DataAnnotation.PrimaryKey.class) {
                cmd.append("NOT NULL PRIMARY KEY  ");
            } else if (annotation.annotationType() == DataAnnotation.NotNull.class) {
                cmd.append("NOT NULL ");
            } else if (annotation.annotationType() == DataAnnotation.Unique.class) {
                cmd.append("UNIQUE ");
            } else if (annotation.annotationType() == DataAnnotation.Default.class) {
                DataAnnotation.Default da = field.getAnnotation(DataAnnotation.Default.class);
                cmd.append("DEFAULT ").append(da.value()).append(" ");
            }
        }
        return cmd.toString();
    }

    /**
     * 解析字段在数据中名称
     *
     * @param field
     */
    private static String parseFieldInDbName(Field field) {
        String inDbName;
        if (field.getAnnotation(DataAnnotation.InDbName.class) == null) {
            inDbName = field.getName();
        } else {
            inDbName = field.getAnnotation(DataAnnotation.InDbName.class).value();
        }
        if (!field.isAccessible()) {
            field.setAccessible(true);
        }
        return TextUtils.isEmpty(inDbName) ? field.getName() : inDbName;
    }

    /**
     * 获取对象对应的表名
     * TableName注解值或者类名(大写)
     *
     * @param entryClass
     * @return
     */
    protected static String getTabName(Class<?> entryClass) {
        String tabName;
        if (null == entryClass.getAnnotation(DataAnnotation.TableName.class)) {
            tabName = entryClass.getSimpleName();
        } else {
            tabName = entryClass.getAnnotation(DataAnnotation.TableName.class).name();
        }
        return tabName.toUpperCase();
    }

    /**
     * 数据库中字段名称和对象中成员变量名称的映射关系
     *
     * @param entryClass
     * @return key 数据库中的名称  val 字段
     */
    private static HashMap<String, Field> mapField(Class<?> entryClass) {
        HashMap<String, Field> map = new HashMap<>(8);
        Field[] fields = entryClass.getDeclaredFields();
        String inDbName;
        for (Field field : fields) {
            if (isFieldSaveToDb(field)) {
                inDbName = parseFieldInDbName(field);
                map.put(inDbName, field);
            }
        }
        return map;
    }

    /**
     * 构建查询条件
     *
     * @param argMaps key:数据库中字段的名称  val:对应字段的值
     * @return String whereCasue："name=? and password=?"
     * String[] whereArgs：new String[]{"tys"}
     * 注意：argMaps.size 为0时 返回 “” new String[]
     */
    private static DaoSqlImpl.Condition buildCondition(HashMap<String, String> argMaps) {
        StringBuilder whereCasueSb = new StringBuilder();
        String[] whereArgs = new String[argMaps.size()];
        int i = 0;
        for (Map.Entry<String, String> val : argMaps.entrySet()) {
            whereCasueSb.append(val.getKey()).append("=? ").append("and ");
            whereArgs[i++] = val.getValue();
        }
        DaoSqlImpl.Condition where = new DaoSqlImpl.Condition();
        whereCasueSb.delete(whereCasueSb.length() - 4, whereCasueSb.length());
        where.setWhereClaus(whereCasueSb.toString());
        where.setWhereArgs(whereArgs);
        return where;
    }

    /**
     * 将一个对象转换成ContentValues，PrimaryKey和null时不设置值
     *
     * @param entry
     * @return
     */
    public static ContentValues obj2cv(Object entry) {
        ContentValues cv = new ContentValues();
        HashMap<String, Field> map = DbHelper.mapField(entry.getClass());
        for (Map.Entry<String, Field> val : map.entrySet()) {
            try {
                if (null != val.getValue().getAnnotation(DataAnnotation.PrimaryKey.class)) {
                    continue;
                }
                Object vObj = val.getValue().get(entry);
                if (vObj == null) {
                    continue;
                }
                Class<?> type = vObj.getClass();
                if (type == String.class) {
                    cv.put(val.getKey(), String.valueOf(vObj));
                } else if (type == Integer.class) {
                    cv.put(val.getKey(), (int) vObj);
                } else if (type == Boolean.class) {
                    cv.put(val.getKey(), (boolean) vObj);
                } else if (type == Long.class) {
                    cv.put(val.getKey(), (long) vObj);
                } else if (type == Double.class) {
                    cv.put(val.getKey(), (double) vObj);
                } else if (type == Float.class) {
                    cv.put(val.getKey(), (float) vObj);
                } else if (type == byte[].class) {
                    cv.put(val.getKey(), (byte[]) vObj);
                } else if (type == Short.class) {
                    cv.put(val.getKey(), (short) vObj);
                } else {
                    Log.e(TAG, "UnSupport type:" + type.getName());
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return cv;
    }

    /**
     * 从cursor里面提取出所有对象
     *
     * @param <T>
     * @param cls
     * @param fields
     * @param cursor @return
     */
    public static <T> List<T> cursor2Obj(Class<?> cls, List<Field> fields, Cursor cursor) {
        ArrayList<T> list = new ArrayList<>();
        Object o = null;
        while (cursor.moveToNext()) {
            try {
                o = cls.newInstance();
                for (Field field : fields) {
                    int index = cursor.getColumnIndex(parseFieldInDbName(field));
                    Class<?> type = field.getType();
                    if (type == String.class) {
                        field.set(o, cursor.getString(index));
                    } else if (type == Integer.class) {
                        field.set(o, cursor.getInt(index));
                    } else if (type == Boolean.class) {
                        field.set(o, cursor.getShort(index) != 0);
                    } else if (type == Long.class) {
                        field.set(o, cursor.getLong(index));
                    } else if (type == Double.class) {
                        field.set(o, cursor.getDouble(index));
                    } else if (type == Float.class) {
                        field.set(o, cursor.getFloat(index));
                    } else if (type == byte[].class) {
                        field.set(o, cursor.getBlob(index));
                    } else if (type == Short.class) {
                        field.set(o, cursor.getShort(index));
                    } else {
                        Log.e(TAG, "UnSupport type:" + type.getName());
                    }
                }
                list.add((T) o);
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return list;
    }

    /**
     * 获取一对象的唯一条件，判断的条件如下
     * 1.PrimaryKey存在
     * 2.Unique存在
     *
     * @param entry
     * @param <T>
     * @return
     */
    public static <T> DaoSqlImpl.Condition uniqueObj(T entry) {
        //key:数据库中字段的名称  val:对应字段entry的值
        HashMap<String, String> temp = new HashMap<>(5);
        Field[] fields = entry.getClass().getDeclaredFields();
        for (Field field : fields) {
            try {
                if (field.getAnnotation(DataAnnotation.PrimaryKey.class) != null ||
                        field.getAnnotation(DataAnnotation.Unique.class) != null) {
                    field.setAccessible(true);
                    Object obj = field.get(entry);
                    if (obj == null) {
                        continue;
                    }
                    temp.put(parseFieldInDbName(field), String.valueOf(obj));
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        if (temp.size() <= 0) {
            return null;
        }
        return buildCondition(temp);
    }

    /**
     * 查询条件
     *
     * @param <T>
     * @param entry
     * @return
     */
    public static <T> DaoSqlImpl.Condition queryWhere(T entry) {
        //key:数据库中字段的名称  val:对应字段entry的值
        HashMap<String, String> temp = new HashMap<>(8);
        Field[] fields = entry.getClass().getDeclaredFields();
        for (Field field : fields) {
            try {
                if (!isFieldSaveToDb(field)) {
                    continue;
                }
                field.setAccessible(true);
                Object obj = field.get(entry);
                if (obj == null) {
                    continue;
                }
                temp.put(parseFieldInDbName(field), String.valueOf(obj));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        if (temp.size() <= 0) {
            return null;
        }
        return buildCondition(temp);
    }

    /**
     * 构建查询条件limit
     *
     * @param offset
     * @param limit
     */
    public static String queryLimit(Integer offset, Integer limit) {
        String l;
        if (offset == null && limit == null) {
            l = null;
        } else if (offset == null) {
            l = String.valueOf(limit);
        } else if (limit == null) {
            l = offset + "," + Integer.MAX_VALUE;
        } else {
            l = offset + "," + limit;
        }
        return l;
    }

    /**
     * 获取对象 存入数据库中的所有字段
     *
     * @param cls
     */
    public static List<Field> parseFields(Class<?> cls) {
        ArrayList<Field> listFiled = new ArrayList<>();
        Field[] fields = cls.getDeclaredFields();
        for (Field field : fields) {
            if (isFieldSaveToDb(field)) {
                if (!field.isAccessible()) {
                    field.setAccessible(true);
                }
                listFiled.add(field);
            }
        }
        return listFiled;
    }
}
